package com.zenghus.spring.servlet;

import com.zenghus.demo.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zenghu
 * @date 2018/4/28 11:32
 */
public class DispatchServlet extends HttpServlet{
    private Properties p=new Properties();

    //存放所有到的类地址
    private List<String> classNames=new ArrayList<String>();

    //定义一个ioc容器
    private Map<String, Object> ioc=new HashMap<String, Object>();

    //存放访问请求对应的方法
    private List<Handler> HandlerMapping=new ArrayList<Handler>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub

        //1：加载配置文件application.properties
        String path=config.getInitParameter("contextConfigLocation");//获取配置的路径
        doLoadConfig(path);

        //2：扫描相关的类
        doScanner(p.getProperty("scanPackage"));

        //3：初始化所有相关的类添加到ioc容器中（自己实现ioc容器）
        doInstance();

        //4：实现依赖注入
        doAutowired();

        //5：构造一个HandlerMapping，将配置好的url和一个Method建立关系
        initHandlerMapping();

        //6：等待请求，调用doGet或者doPost方法，将匹配的url去动态调用对应的方法


        System.out.println("My Mvc Framework is start");
    }

    private void doLoadConfig(String path){
        //根据路径加载配置文件
        InputStream is=this.getClass().getClassLoader().getResourceAsStream(path.replace("classpath:",""));
        try {
            p.load(is);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            try {
                is.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private void doScanner(String packageName){
        //递归获取包下所有文件
        URL url=this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
        File dir=new File(url.getFile());
        for (File file : dir.listFiles()) {
            if(file.isDirectory()){//判断是否是文件夹，如果是的话就递归
                doScanner(packageName+"."+file.getName());
            }else{
                //获取文件名称
                String className=packageName+"."+file.getName().replace(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doInstance(){
        if(classNames.size()==0)
            return ;
        try {
            //循环所有扫描出来的类名，实例化，并且放到ioc容器中去
            for(String classname:classNames){
                Class clazz=Class.forName(classname);
                //如果加了这个注解的话就认为是一个controller去初始化
                if(clazz.isAnnotationPresent(Controller.class)){
                    //利用反射初始化
                    //<bean id="beanName" class="">
                    String beanName=lowerFirst(clazz.getSimpleName());
                    ioc.put(beanName, clazz.newInstance());

                    //如果加了这个注解的类，也是可以初始化的
                }else if(clazz.isAnnotationPresent(Service.class)){
                    //1：如果自己起了名字，优先使用自己的
                    //2：如果没起名字，默认首字母小写
                    //3：如果对象注入的是接口，采用其实现接口的全程作为beanName

                    Service service=(Service) clazz.getAnnotation(Service.class);
                    String beanname=service.value();
                    if(!"".equals(beanname)){
                        ioc.put(beanname, clazz.newInstance());
                    }else{
                        beanname=lowerFirst(clazz.getSimpleName());
                        ioc.put(beanname, clazz.newInstance());
                    }

                    //获取当前对象的接口，给接口注入当前实例
                    Class<?>[] interfaces=clazz.getInterfaces();
                    for (Class<?> i : interfaces) {
                        ioc.put(i.getName(), clazz.newInstance());
                    }
                }else{
                    continue;
                }
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    private void doAutowired(){
        if(ioc.isEmpty()){
            return;
        }

        //迭代所有的实例化类
        //注入就是对类中定义的属性进行赋值，不管公有还是私有的
        for(Map.Entry<String, Object> entry:ioc.entrySet()){
            Field[] field= entry.getValue().getClass().getDeclaredFields();
            for (Field field2 : field) {
                if(!field2.isAnnotationPresent(Autowired.class)){
                    continue;
                }
                Autowired autowired=field2.getAnnotation(Autowired.class);
                String beanname=autowired.value().trim();
                if("".equals(beanname)){
                    beanname=field2.getType().getName();
                }
                //给Autowired赋值
                field2.setAccessible(true);

                try {
                    //通过反射强制赋值
                    field2.set(entry.getValue(), ioc.get(beanname));
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                field2.setAccessible(false);
            }
        }
    }

    //将url跟method进行关联
    private void initHandlerMapping(){
        if(ioc.isEmpty()){
            return;
        }

        for(Map.Entry<String, Object> entry:ioc.entrySet()){
            Class<?> cla=entry.getValue().getClass();
            //如果不是controller不做操作
            if(!cla.isAnnotationPresent(Controller.class)){
                continue;
            }

            //获取controller本身是否有requestmapping
            String baseurl="";
            if(cla.isAnnotationPresent(RequestMapping.class)){
                RequestMapping rm=cla.getAnnotation(RequestMapping.class);
                baseurl=rm.value();
            }

            //扫描所有的public方法
            Method [] methods= entry.getValue().getClass().getMethods();
            for (Method method : methods) {
                if(!method.isAnnotationPresent(RequestMapping.class)){
                    continue;
                }
                RequestMapping rm =method.getAnnotation(RequestMapping.class);

                //把url和method的关系重新封装一次
                String mappingurl=("/"+baseurl+rm.value()).replace("//", "/");
                Pattern p=Pattern.compile(mappingurl);
                HandlerMapping.add(new Handler(p, entry.getValue(), method));
            }
        }
    }

    //首字母小写
    private String lowerFirst(String str){
        char[] chars=str.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            // TODO: handle exception
            resp.getWriter().write("500 Exception,Details:"+Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
        //拿到当前请求的url
        String url=req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath, "").replaceAll("/+", "/");

        Handler handler=getHandler(req);
        if(handler==null){
            resp.getWriter().write("404 Not Found");
        }

        Class<?>[] paramTypes=handler.methor.getParameterTypes();

        //保存所有需要自动赋值的参数值
        Object[] paramValues=new Object[paramTypes.length];

        //获取所有的参数
        Map<String, String[]> params=req.getParameterMap();
        for (Map.Entry<String, String[]> param : params.entrySet()) {
            String value=Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "");

            //如果能匹配到对象，开始填充参数值
            if(!handler.paramIndexMapping.containsKey(param.getKey()))
                continue;
            int index=handler.paramIndexMapping.get(param.getKey());
            paramValues[index]=convet(paramTypes[index], value);
        }

        //设置方法中的request和response对象
        int reqIndex=handler.paramIndexMapping.get(HttpServletRequest.class.getName());
        paramValues[reqIndex]=req;
        int respIndex=handler.paramIndexMapping.get(HttpServletResponse.class.getName());
        paramValues[respIndex]=resp;

        //通过反射调用方法
        handler.methor.invoke(handler.controller, paramValues);

    }

    private Handler getHandler(HttpServletRequest request) throws Exception{
        if(HandlerMapping.isEmpty())
            return null;
        //拿到当前请求的url
        String url=request.getRequestURI();
        String contextPath=request.getContextPath();
        url=url.replace(contextPath, "").replaceAll("/+", "/");
        for (Handler handler : HandlerMapping) {
            //匹配url
            Matcher matcher=handler.pattern.matcher(url);
            //如果没有匹配上就继续下一次匹配
            if(!matcher.matches()){
                continue;
            }
            return handler;
        }
        return null;
    }

    private Object convet(Class<?> type,String value){
        if(Integer.class==type){
            return Integer.valueOf(value);
        }
        return value;
    }


    private class Handler{
        protected Object controller;//存放方法对应的实例
        protected Method methor;//存放方法
        protected Pattern pattern;//支持正则的url
        protected Map<String, Integer> paramIndexMapping=new HashMap<String, Integer>();//参数顺序


        /**
         * 构造一个handler基本参数
         * @param pattern
         * @param controller
         * @param method
         */
        protected Handler(Pattern pattern,Object controller,Method method){
            this.controller=controller;
            this.methor=method;
            this.pattern=pattern;
            paramIndexMapping(method);
        }

        private void paramIndexMapping(Method method){
            //获取方法的参数注解（因为参数前可以添加多个注解,所以是二维数组,一个参数上不可以添加相同的注解,同一个注解可以加在不同的参数上!）
            Annotation[][] pa=method.getParameterAnnotations();
            for (int i=0;i<pa.length;i++) {
                for (Annotation annotation : pa[i]) {
                    if(annotation instanceof RequestParam){//判断是否是自定义的参数注解
                        //获取参数注解的值
                        String paramname=((RequestParam)annotation).value();
                        if(!"".equals(paramname.trim())){
                            paramIndexMapping.put(paramname, i);
                        }
                    }
                }
            }

            //获取方法中的request和response参数
            Class<?>[] paramsTypes=method.getParameterTypes();
            for(int i=0;i<paramsTypes.length;i++){
                Class<?> type=paramsTypes[i];
                if(type==HttpServletRequest.class || type==HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(), i);
                }

            }
        }
    }
}
